﻿#include "logfileprinter.h"
#include "_private/_privateutil.hpp"
#include <filesystem>

NAMESPACE_USE
LoggerFilePrinter::LoggerFilePrinter()
{
    __initFileHand();
   
}

LoggerFilePrinter::~LoggerFilePrinter()
{

    for (auto& it : file_hands_) {
        it.second->file_hand_->close();
    }
    file_hands_.clear();
}


void LoggerFilePrinter::__initFileHand() {
    auto mins = std::to_string(std::chrono::duration_cast<std::chrono::minutes>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
    std::stringstream file_suffix;
    file_suffix << "_" << mins;
    auto debug_hand = __creatFilehand(LOG_LEVEL::BUG, "debug.log"+file_suffix.str());
    file_hands_.insert(std::make_pair(LOG_LEVEL::BUG, debug_hand));

    auto fat = __creatFilehand(LOG_LEVEL::FAT, "fat.log"+file_suffix.str());
    file_hands_.insert(std::make_pair(LOG_LEVEL::FAT, fat));

    auto info_hand = __creatFilehand(LOG_LEVEL::INF, "info.log"+file_suffix.str());
    file_hands_.insert(std::make_pair(LOG_LEVEL::INF, info_hand));

    auto error_hand = __creatFilehand(LOG_LEVEL::ERR, "error.log"+file_suffix.str());
    file_hands_.insert(std::make_pair(LOG_LEVEL::ERR, error_hand));

    auto waring_hand = __creatFilehand(LOG_LEVEL::WAR, "waring.log"+file_suffix.str());
    file_hands_.insert(std::make_pair(LOG_LEVEL::WAR, waring_hand));

}

FileHand* LoggerFilePrinter::__creatFilehand(LOG_LEVEL level, std::string file_name) {
    std::filesystem::create_directories (file_dir_);
    size_t write_cnt = 0;
    auto file_path = file_dir_ + "/" + __getToDaydDate() + "_" + file_name;
    if (std::filesystem::exists (file_path)) {
        size_t size = std::filesystem::file_size (file_path);
        if (file_max_size_ * (1 + 0.01) <= size) {
            std::string backup_file_path = file_path;
            backup_file_path += std::to_string (std::chrono::system_clock::now ().time_since_epoch ().count ());
            std::rename (file_path.c_str (), backup_file_path.c_str ());
        }
        else {
            write_cnt = size;
        }
    }
    std::ofstream out (file_path, std::ios::out | std::ios::binary | std::ios::app);
    auto hand = new FileHand();
    hand->file_hand_ = new std::ofstream(file_path, std::ios::out | std::ios::binary | std::ios::app);
    if (!hand->file_hand_->is_open())
        throw std::runtime_error("cant create log file!" + file_name);
    hand->file_name_ = file_name;
    hand->level_ = level;
    hand->write_cnt_ = write_cnt;
    hand->path_ = file_path;
    return hand;
}


void  LoggerFilePrinter::__setLogFormat(const LogItem& logger) {
    std::stringstream ss;
    if (format_ & LOG_FORMAT::DIS_FILE) {
        ss << "[" << logger.property_.file_ << "]";
    }
    if (format_ & LOG_FORMAT::DIS_THID) {
        ss << "[" << logger.property_.thread_id_ << "]";
    }
    ss << "[";
    if (format_& LOG_FORMAT::DIS_FUN) {
        ss << logger.property_.func_ << ":";
    }
    if (format_ & LOG_FORMAT::DIS_LIN) {
        ss << logger.property_.line_;
    }
    ss << "]";
    if (format_ & LOG_FORMAT::DIS_TIME) {
        ss << "[" << serilTimeToStr(logger.property_.time_) << "]";
    }
    prefix_str_ = std::move(ss.str());
}

void LoggerFilePrinter::print(const LogItem& logger)
{
    __setLogFormat(logger);
    auto hand = file_hands_.find(logger.property_.level_);
    assert(hand != file_hands_.end());
    *(hand->second->file_hand_) << prefix_str_ << logger.log_ << "\r\n";
    hand->second->file_hand_->flush();
    hand->second->write_cnt_ += prefix_str_.size() + logger.log_.size() + 4;
    if (hand->second->write_cnt_ >= file_max_size_) {
        auto file_level = hand->first;
        auto file_hand = hand->second;
        file_hand->file_hand_->close();
        delete file_hand->file_hand_;
        file_hands_.erase(hand);

        auto new_hand = __creatFilehand(file_level, file_hand->file_name_);
        file_hands_.insert(std::make_pair(new_hand->level_, new_hand));
    }
}
